import json
import yaml
import configparser
import xml.etree.ElementTree as ET
from typing import Dict, Any, List, Generator, Optional, Union


class ConfigManager:
    @staticmethod
    def ini_load(filename: str) -> Dict[str, Dict[str, str]]:
        """
        从 INI 格式的配置文件中加载配置项并返回。

        参数：
        - filename: 字符串，要加载的 INI 文件的路径。

        返回值：
        - config_dict: 字典，包含从 INI 文件中加载的配置项。
        """
        config = configparser.ConfigParser()
        config.read(filename, encoding="utf-8")
        config_dict = {}
        for section in config.sections():
            config_dict[section] = dict(config.items(section))
        return config_dict

    @staticmethod
    def ini_dump(data: Dict[str, Dict[str, Any]], filename: str) -> None:
        """
        将配置项写入到 INI 文件中。

        参数：
        - data: 字典，包含要写入到 INI 文件的配置项。格式为 {section: {key: value}}
        - filename: 字符串，要写入的 INI 文件的路径。
        """
        config = configparser.ConfigParser()
        
        for section, options in data.items():
            config[section] = {}
            for key, value in options.items():
                config[section][key] = str(value)
        
        with open(filename, 'w', encoding="utf-8") as configfile:
            config.write(configfile)

    @staticmethod
    def ini_load_string(data: str) -> Dict[str, Dict[str, str]]:
        """
        从 INI 格式的字符串中加载配置项并返回。

        参数：
        - data: 字符串，包含 INI 格式的配置项。

        返回值：
        - config_dict: 字典，包含从 INI 字符串中加载的配置项。
        """
        config = configparser.ConfigParser()
        config.read_string(data)
        config_dict = {}
        for section in config.sections():
            config_dict[section] = dict(config.items(section))
        return config_dict

    @staticmethod
    def ini_dump_string(data: Dict[str, Dict[str, Any]]) -> str:
        """
        将配置项转换为 INI 格式的字符串并返回。

        参数：
        - data: 字典，包含要转换为 INI 字符串的配置项。

        返回值：
        - ini_str: 字符串，包含 INI 格式的配置项。
        """
        config = configparser.ConfigParser()
        
        for section, options in data.items():
            config[section] = {}
            for key, value in options.items():
                config[section][key] = str(value)
        
        # 使用 StringIO 来捕获输出
        from io import StringIO
        output = StringIO()
        config.write(output)
        return output.getvalue()

    @staticmethod
    def json_load(filename: str) -> Dict[str, Any]:
        """
        从 JSON 格式的配置文件中加载配置项并返回。

        参数：
        - filename: 字符串，要加载的 JSON 文件的路径。

        返回值：
        - config_dict: 字典，包含从 JSON 文件中加载的配置项。
        """
        with open(file=filename, mode='r', encoding="utf-8") as file:
            return json.load(fp=file)

    @staticmethod
    def json_loads(data: str) -> Dict[str, Any]:
        """
        从 JSON 格式的字符串中加载配置项并返回。

        参数：
        - data: 字符串，包含 JSON 格式的配置项。

        返回值：
        - config_dict: 字典，包含从 JSON 字符串中加载的配置项。
        """
        return json.loads(s=data)

    @staticmethod
    def json_dump(data: Dict[str, Any], filename: str, ensure_ascii=False) -> None:
        """
        将配置项写入到 JSON 文件中。

        参数：
        - data: 字典，包含要写入到 JSON 文件的配置项。
        - filename: 字符串，要写入的 JSON 文件的路径。
        - ensure_ascii: 布尔值，是否将非 ASCII 字符转为 Unicode 字符。如果包含有中文就选择False。
        """
        with open(file=filename, mode='w', encoding="utf-8") as fileobject:
            json.dump(obj=data, fp=fileobject, indent=4, ensure_ascii=ensure_ascii)

    @staticmethod
    def json_dumps(data: Dict[str, Any], ensure_ascii=False) -> str:
        """
        将配置项转换为 JSON 格式的字符串并返回。

        参数：
        - data: 字典，包含要转换为 JSON 字符串的配置项。
        - ensure_ascii: 布尔值，是否将非 ASCII 字符转为 Unicode 字符。如果包含有中文就选择False。

        返回值：
        - json_str: 字符串，包含 JSON 格式的配置项。
        """
        return json.dumps(data, indent=4, ensure_ascii=ensure_ascii)

    @staticmethod
    def yaml_load(filename: str) -> Dict[str, Any]:
        """
        从 YAML 格式的配置文件中加载配置项并返回。

        参数：
        - filename: 字符串，要加载的 YAML 文件的路径。

        返回值：
        - config_dict: 字典，包含从 YAML 文件中加载的配置项。
        """
        with open(file=filename, mode='r', encoding="utf-8") as fileobject:
            return yaml.load(stream=fileobject, Loader=yaml.Loader)

    @staticmethod
    def yaml_load_all(filename: str) -> Generator[Dict[str, Any], None, None]:
        """
        从 YAML 格式的配置文件中加载多个文档并返回生成器。

        参数：
        - filename: 字符串，要加载的 YAML 文件的路径。

        返回值：
        - generator: 生成器，用于逐个生成从 YAML 文件中加载的配置项。
        """
        with open(file=filename, mode='r', encoding="utf-8") as fileobject:
            return yaml.load_all(stream=fileobject, Loader=yaml.Loader)

    @staticmethod
    def yaml_safe_load(filename: str) -> Dict[str, Any]:
        """
        从 YAML 格式的配置文件中安全加载配置项并返回。

        参数：
        - filename: 字符串，要加载的 YAML 文件的路径。

        返回值：
        - config_dict: 字典，包含从 YAML 文件中加载的配置项。
        """
        with open(file=filename, mode='r', encoding="utf-8") as fileobject:
            return yaml.safe_load(stream=fileobject)

    @staticmethod
    def yaml_safe_load_all(filename: str) -> Generator[Dict[str, Any], None, None]:
        """
        从 YAML 格式的配置文件中安全加载多个文档并返回生成器。

        参数：
        - filename: 字符串，要加载的 YAML 文件的路径。

        返回值：
        - generator: 生成器，用于逐个生成从 YAML 文件中加载的配置项。
        """
        with open(file=filename, mode='r', encoding="utf-8") as fileobject:
            return yaml.safe_load_all(stream=fileobject)

    @staticmethod
    def yaml_dump(data: Dict[str, Any], filename: str, **kwargs) -> None:
        """
        将配置项写入到 YAML 文件中。

        参数：
        - data: 字典，包含要写入到 YAML 文件的配置项。
        - filename: 字符串，要写入的 YAML 文件的路径。
        - **kwargs: 其他传递给 yaml.dump 的参数。
        """
        with open(file=filename, mode='w', encoding="utf-8") as fileobject:
            yaml.dump(data=data, stream=fileobject, **kwargs)

    @staticmethod
    def yaml_dump_all(data: List[Dict[str, Any]], filename: str, **kwargs) -> None:
        """
        将多个配置项写入到 YAML 文件中。

        参数：
        - data: 列表，包含要写入到 YAML 文件的多个配置项。
        - filename: 字符串，要写入的 YAML 文件的路径。
        - **kwargs: 其他传递给 yaml.dump_all 的参数。
        """
        with open(file=filename, mode='w', encoding="utf-8") as fileobject:
            yaml.dump_all(documents=data, stream=fileobject, **kwargs)

    @staticmethod
    def yaml_safe_dump(data: Dict[str, Any], filename: str, **kwargs) -> None:
        """
        将配置项安全地写入到 YAML 文件中。

        参数：
        - data: 字典，包含要写入到 YAML 文件的配置项。
        - filename: 字符串，要写入的 YAML 文件的路径。
        - **kwargs: 其他传递给 yaml.safe_dump 的参数。
        """
        with open(file=filename, mode='w', encoding="utf-8") as fileobject:
            yaml.safe_dump(data=data, stream=fileobject, **kwargs)

    @staticmethod
    def yaml_safe_dump_all(data: List[Dict[str, Any]], filename: str, **kwargs) -> None:
        """
        将多个配置项安全地写入到 YAML 文件中。

        参数：
        - data: 列表，包含要写入到 YAML 文件的多个配置项。
        - filename: 字符串，要写入的 YAML 文件的路径。
        - **kwargs: 其他传递给 yaml.safe_dump_all 的参数。
        """
        with open(file=filename, mode='w', encoding="utf-8") as fileobject:
            yaml.safe_dump_all(documents=data, stream=fileobject, **kwargs)

    # XML 配置文件支持
    @staticmethod
    def xml_load(filename: str) -> Dict[str, Any]:
        """
        从 XML 格式的配置文件中加载配置项并返回。

        参数：
        - filename: 字符串，要加载的 XML 文件的路径。

        返回值：
        - config_dict: 字典，包含从 XML 文件中加载的配置项。
        """
        tree = ET.parse(filename)
        root = tree.getroot()
        return ConfigManager._xml_to_dict(root)

    @staticmethod
    def xml_loads(data: str) -> Dict[str, Any]:
        """
        从 XML 格式的字符串中加载配置项并返回。

        参数：
        - data: 字符串，包含 XML 格式的配置项。

        返回值：
        - config_dict: 字典，包含从 XML 字符串中加载的配置项。
        """
        root = ET.fromstring(data)
        return ConfigManager._xml_to_dict(root)

    @staticmethod
    def xml_dump(data: Dict[str, Any], filename: str, root_tag: str = 'config') -> None:
        """
        将配置项写入到 XML 文件中。

        参数：
        - data: 字典，包含要写入到 XML 文件的配置项。
        - filename: 字符串，要写入的 XML 文件的路径。
        - root_tag: 字符串，XML 根元素的标签名，默认为 'config'。
        """
        root = ConfigManager._dict_to_xml(data, root_tag)
        tree = ET.ElementTree(root)
        
        # 美化 XML 输出
        ConfigManager._indent_xml(root)
        
        with open(filename, 'w', encoding="utf-8") as fileobject:
            tree.write(fileobject, encoding='unicode', xml_declaration=True)

    @staticmethod
    def xml_dumps(data: Dict[str, Any], root_tag: str = 'config') -> str:
        """
        将配置项转换为 XML 格式的字符串并返回。

        参数：
        - data: 字典，包含要转换为 XML 字符串的配置项。
        - root_tag: 字符串，XML 根元素的标签名，默认为 'config'。

        返回值：
        - xml_str: 字符串，包含 XML 格式的配置项。
        """
        root = ConfigManager._dict_to_xml(data, root_tag)
        
        # 美化 XML 输出
        ConfigManager._indent_xml(root)
        
        return ET.tostring(root, encoding='unicode')

    @staticmethod
    def _xml_to_dict(element: ET.Element) -> Dict[str, Any]:
        """
        将 XML 元素转换为字典。

        参数：
        - element: XML 元素。

        返回值：
        - dict_obj: 字典，包含 XML 元素的转换结果。
        """
        result = {}
        
        # 处理属性
        if element.attrib:
            result['@attributes'] = element.attrib
        
        # 处理子元素
        children = list(element)
        if children:
            for child in children:
                child_data = ConfigManager._xml_to_dict(child)
                
                # 处理重复元素
                if child.tag in result:
                    if not isinstance(result[child.tag], list):
                        result[child.tag] = [result[child.tag]]
                    result[child.tag].append(child_data)
                else:
                    result[child.tag] = child_data
        else:
            # 叶子节点，存储文本内容
            if element.text and element.text.strip():
                result['#text'] = element.text.strip()
        
        # 如果没有子元素和属性，只有文本，直接返回文本
        if len(result) == 1 and '#text' in result:
            return result['#text']
        
        return result

    @staticmethod
    def _dict_to_xml(data: Any, tag: str) -> ET.Element:
        """
        将字典转换为 XML 元素。

        参数：
        - data: 要转换的数据。
        - tag: 元素的标签名。

        返回值：
        - element: XML 元素。
        """
        element = ET.Element(tag)
        
        if isinstance(data, dict):
            # 处理属性
            if '@attributes' in data:
                for key, value in data['@attributes'].items():
                    element.set(key, str(value))
                # 移除已处理的属性
                data = {k: v for k, v in data.items() if k != '@attributes'}
            
            # 处理文本内容
            if '#text' in data:
                element.text = str(data['#text'])
                # 移除已处理的文本
                data = {k: v for k, v in data.items() if k != '#text'}
            
            # 处理子元素
            for key, value in data.items():
                if isinstance(value, list):
                    # 列表类型的值，创建多个同名元素
                    for item in value:
                        child = ConfigManager._dict_to_xml(item, key)
                        element.append(child)
                else:
                    child = ConfigManager._dict_to_xml(value, key)
                    element.append(child)
        else:
            # 基本数据类型
            element.text = str(data)
        
        return element

    @staticmethod
    def _indent_xml(element: ET.Element, level: int = 0) -> None:
        """
        美化 XML 输出，添加缩进。

        参数：
        - element: XML 元素。
        - level: 缩进级别。
        """
        # 计算缩进
        indent = "\n" + level * "  "
        
        if len(element):
            if not element.text or not element.text.strip():
                element.text = indent + "  "
            if not element.tail or not element.tail.strip():
                element.tail = indent
            
            for child in element:
                ConfigManager._indent_xml(child, level + 1)
            
            if not child.tail or not child.tail.strip():
                child.tail = indent
        else:
            if level and (not element.tail or not element.tail.strip()):
                element.tail = indent

    @staticmethod
    def detect_format(filename: str) -> Optional[str]:
        """
        检测配置文件的格式。

        参数：
        - filename: 字符串，配置文件的路径。

        返回值：
        - format_str: 字符串，检测到的格式（'ini', 'json', 'yaml', 'yml', 'xml'），如果无法检测则返回 None。
        """
        if not filename:
            return None
            
        ext = filename.lower().split('.')[-1]
        if ext in ['ini', 'cfg', 'conf']:
            return 'ini'
        elif ext == 'json':
            return 'json'
        elif ext in ['yaml', 'yml']:
            return 'yaml'
        elif ext == 'xml':
            return 'xml'
        else:
            return None

    @staticmethod
    def auto_load(filename: str) -> Any:
        """
        自动检测文件格式并加载配置。

        参数：
        - filename: 字符串，配置文件的路径。

        返回值：
        - config_data: 配置数据，格式取决于文件类型。
        """
        file_format = ConfigManager.detect_format(filename)
        
        if file_format == 'ini':
            return ConfigManager.ini_load(filename)
        elif file_format == 'json':
            return ConfigManager.json_load(filename)
        elif file_format == 'yaml':
            return ConfigManager.yaml_load(filename)
        elif file_format == 'xml':
            return ConfigManager.xml_load(filename)
        else:
            raise ValueError(f"不支持的文件格式: {filename}")


# 使用示例
if __name__ == "__main__":
    # XML 配置文件操作示例
    xml_data = {
        'app': {
            '@attributes': {'version': '1.0.0'},
            'name': 'My Application',
            'settings': {
                'theme': 'dark',
                'language': 'zh-CN',
                'database': {
                    'host': 'localhost',
                    'port': 3306,
                    'credentials': {
                        'username': 'admin',
                        'password': 'secret'
                    }
                }
            },
            'features': [
                {'name': 'feature1', 'enabled': True},
                {'name': 'feature2', 'enabled': False}
            ]
        }
    }
    
    # 写入 XML 文件
    ConfigManager.xml_dump(xml_data, 'test.xml', 'configuration')
    
    # 读取 XML 文件
    loaded_xml = ConfigManager.xml_load('test.xml')
    print("XML 文件内容:")
    print(json.dumps(loaded_xml, ensure_ascii=False, indent=2))
    
    # XML 字符串操作
    xml_string = ConfigManager.xml_dumps(xml_data, 'configuration')
    print("\nXML 字符串:")
    print(xml_string)
    
    # 从字符串加载
    parsed_xml = ConfigManager.xml_loads(xml_string)
    print("\n从字符串解析的 XML:")
    print(json.dumps(parsed_xml, ensure_ascii=False, indent=2))
    
    # 自动检测格式
    format_type = ConfigManager.detect_format('test.xml')
    print(f"\n检测到的格式: {format_type}")
    
    # 自动加载
    auto_loaded = ConfigManager.auto_load('test.xml')
    print("\n自动加载的数据:")
    print(json.dumps(auto_loaded, ensure_ascii=False, indent=2))